package com.ls.dbpool;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.log4j.Logger;

public class DiskCleanManager {
	private static Logger logger = Logger.getLogger(DiskCleanManager.class);
	private static  DiskCleanManager instance = new DiskCleanManager();

	public static DiskCleanManager getIntance() {
		return instance;
	}

	private DiskCleanManager() {
	}

	public void cleanDisk() throws DiskCleanException {
		boolean bottomLine = true;
		int stage = 0;
		Map<String, List<List<String>>> maps = null;
		try {
			maps = getLogTablesNames();
		} catch (SQLException e) {
			logger.error("get logtables name error", e);
			throw new DiskCleanException("get logtables name error", e);
		}
		while (isOverDiskBottomThreshold()) {
			bottomLine = cleanLogs(maps, bottomLine);
			bottomLine = cleanStatistics(stage, bottomLine, 1);
			if (bottomLine) {
				if (isLowDiskTopThreshold()) {
					logger.warn("已达到底线，但是磁盘空间大于最低阈值，小于最高阈值");
				} else {
					logger.warn("已达到底线值，但是磁盘空间还是要满，所以通知管理员查看，并停止logcat");
					notifyAdmin();
					stopLogcat();
				}
				break;
			}
			stage++;
			logger.debug("clean stage is : "+stage);
		}
	}

	/**
	 * TODO cc_statictis 修改表名
	 * 
	 * @param stage
	 * @param bottomLine
	 * @param remainDays
	 *            保留几天的数据
	 */
	private boolean cleanStatistics(int stage, boolean bottomLine, int remainDays) {
		if (stage < 1) {
			String sql = String.format(
					"SELECT count(*) from cc_healthstatistic where to_date(ts, 'YYYY-MM-DD ')<=(now()::date - %d)",
					remainDays);
			String sqlTotal = "SELECT count(*) from cc_healthstatistic";
			try {
				long remainDaysCount = DBUtility.execBasicSelect(sql, Long.class);
				long allDaysCount = DBUtility.execBasicSelect(sqlTotal, Long.class);
				if (remainDaysCount == allDaysCount) {
					bottomLine &= true;
				} else {
					bottomLine &= false;
					String delSql = String.format(
							"delete from  cc_healthstatistic where code in (SELECT code from cc_healthstatistic where to_date(ts, 'YYYY-MM-DD ')<=(now()::date -%d))",
							remainDays);
					String mergeDiskSpace = "vacuum full  cc_healthstatistic";
					DBUtility.execUpdate(delSql);
					DBUtility.execUpdate(mergeDiskSpace);
				}
			} catch (SQLException e) {
				logger.error(e);
			}
		}
		return bottomLine;
	}

	public Map<String, List<List<String>>> getLogTablesNames() throws SQLException {
		String sql = "select tablename from pg_tables where schemaname='public' and tablename ~ 'cc_log_.*_[\\d{2}$|dd_\\d{8}$]' order by tablename ;";
		List<Object[]> rs = DBUtility.execSelect(sql);
		List<String> tableNames = new ArrayList<String>(rs.size());
		for (Object[] s : rs) {
			tableNames.add(s[0].toString());
		}
		Map<String, List<List<String>>> maps = TableNameClassify.classifyTable(tableNames);
		return maps;
	}

	public boolean cleanLogs(Map<String, List<List<String>>> maps, boolean bottomLine) {
		List<List<String>> dayList = maps.get(TableNameClassify.DAY);
		List<List<String>> hourList = maps.get(TableNameClassify.HOUR);
//		bottomLine = cleanLogs(dayList, bottomLine, 1);
		bottomLine = cleanLogs(hourList, bottomLine, 3);
		return bottomLine;
	}

	private boolean cleanLogs(List<List<String>> list, boolean bottomLine, int remain) {
		String sqlFormat = "truncate table %s";
		String sql;
		if (!list.isEmpty()) {
			for (List<String> yyDayList : list) {
				if (yyDayList.size() > remain) {
					bottomLine &= false;
					String oldestDayTable = yyDayList.remove(0);
					sql = String.format(sqlFormat, oldestDayTable);
					try {
						DBUtility.execUpdate(sql);
					} catch (SQLException e) {
						logger.error("truncate table failed " + oldestDayTable, e);
					}
				}
			}
		} else {
			bottomLine &= true;
		}
		return bottomLine;
	}

	private void stopLogcat() {
		// TODO Auto-generated method stub

	}

	private void notifyAdmin() {
		// TODO Auto-generated method stub

	}

	public boolean isOverDiskTopThreshold() {
		Pair<Float, Float> pair = getDiskSize();
		Pair<Float, Float> threshold =  getDiskTopThreshold();
		if (pair.getKey() > threshold.getKey() || pair.getValue() < threshold.getValue()) {
			return true;
		}
		return false;
	}

	private boolean isLowDiskTopThreshold() {
		Pair<Float, Float> pair = getDiskSize();
		Pair<Float, Float> threshold =  getDiskTopThreshold();
		if (pair.getKey() < threshold.getKey() && pair.getValue() > threshold.getValue()) {
			return true;
		}
		return false;
	}

	private boolean isOverDiskBottomThreshold() {
		Pair<Float, Float> pair = getDiskSize();
		Pair<Float, Float> threshold =  getDiskBottomThreshold();
		if (pair.getKey() > threshold.getKey() || pair.getValue() <  threshold.getValue()) {
			return true;
		}
		return false;
	}

	// TODO 获取磁盘使用率
	public Pair<Float, Float> getDiskSize() {
		Pair<Float, Float> pair = Pair.of(0.95f, 5f);
		return pair;
	}

	// TODO 获取磁盘阈值
	public Pair<Float, Float> getDiskBottomThreshold() {
		Pair<Float, Float> pair = Pair.of(0.9f, 10f);
		return pair;
	}

	// TODO 获取磁盘阈值
	public Pair<Float, Float> getDiskTopThreshold() {
		Pair<Float, Float> pair = Pair.of(0.95f, 5f);
		return pair;
	}

}
